@@ -37,10 +37,16 @@ const unlink = util.promisify(fs.unlink);
37
37
const BIGQUERY_VALID_CHARACTERS = / ^ [ a - z A - Z 0 - 9 _ ] + $ / ;
38
38
const FIRESTORE_VALID_CHARACTERS = / ^ [ ^ \/ ] + $ / ;
39
39
40
- const validateInput = ( value : any , name : string , regex : RegExp ) => {
40
+ const FIRESTORE_COLLECTION_NAME_MAX_CHARS = 6144 ;
41
+ const BIGQUERY_RESOURCE_NAME_MAX_CHARS = 1024 ;
42
+
43
+ const validateInput = ( value : string , name : string , regex : RegExp , sizeLimit : number ) => {
41
44
if ( ! value || value === "" || value . trim ( ) === "" ) {
42
45
return `Please supply a ${ name } ` ;
43
46
}
47
+ if ( value . length >= sizeLimit ) {
48
+ return `${ name } must be at most ${ sizeLimit } characters long` ;
49
+ }
44
50
if ( ! value . match ( regex ) ) {
45
51
return `The ${ name } must only contain letters or spaces` ;
46
52
}
@@ -53,36 +59,31 @@ const questions = [
53
59
name : "projectId" ,
54
60
type : "input" ,
55
61
validate : ( value ) =>
56
- validateInput ( value , "project ID" , FIRESTORE_VALID_CHARACTERS ) ,
62
+ validateInput ( value , "project ID" , FIRESTORE_VALID_CHARACTERS , FIRESTORE_COLLECTION_NAME_MAX_CHARS ) ,
57
63
} ,
58
64
{
59
65
message : "What is the path of the the Cloud Firestore Collection you would like to import from? " +
60
66
"(This may, or may not, be the same Collection for which you plan to mirror changes.)" ,
61
67
name : "sourceCollectionPath" ,
62
68
type : "input" ,
63
69
validate : ( value ) =>
64
- validateInput ( value , "collection path" , FIRESTORE_VALID_CHARACTERS ) ,
70
+ validateInput ( value , "collection path" , FIRESTORE_VALID_CHARACTERS , FIRESTORE_COLLECTION_NAME_MAX_CHARS ) ,
65
71
} ,
66
72
{
67
73
message :
68
- "What is the ID of the BigQuery dataset that you would like to use? (The dataset will be created if it doesn't already exist)" ,
74
+ "What is the ID of the BigQuery dataset that you would like to use? (A dataset will be created if it doesn't already exist)" ,
69
75
name : "datasetId" ,
70
76
type : "input" ,
71
77
validate : ( value ) =>
72
- validateInput ( value , "dataset" , BIGQUERY_VALID_CHARACTERS ) ,
78
+ validateInput ( value , "dataset" , BIGQUERY_VALID_CHARACTERS , BIGQUERY_RESOURCE_NAME_MAX_CHARS ) ,
73
79
} ,
74
- ] ;
75
-
76
- const questionsRemaining = ( sourceCollectionPath : string ) => ( [
77
80
{
78
81
message :
79
- "What is the name of the Cloud Firestore Collection you are mirroring? " +
80
- "(Documents in the source Collection will be written to the raw changelog for this Collection.)" ,
81
- name : "destinationCollectionPath" ,
82
+ "What is the identifying prefix of the BigQuery table that you would like to import to? (A table will be created if one doesn't already exist)" ,
83
+ name : "tableId" ,
82
84
type : "input" ,
83
- default : sourceCollectionPath ,
84
85
validate : ( value ) =>
85
- validateInput ( value , "dataset " , BIGQUERY_VALID_CHARACTERS ) ,
86
+ validateInput ( value , "table " , BIGQUERY_VALID_CHARACTERS , BIGQUERY_RESOURCE_NAME_MAX_CHARS ) ,
86
87
} ,
87
88
{
88
89
message :
@@ -94,27 +95,19 @@ const questionsRemaining = (sourceCollectionPath: string) => ([
94
95
return parseInt ( value , 10 ) > 0
95
96
}
96
97
} ,
97
- ] ) ;
98
+ ] ;
98
99
99
100
const run = async ( ) : Promise < number > => {
100
101
const {
101
102
projectId,
102
103
sourceCollectionPath,
103
104
datasetId,
104
- } = await inquirer . prompt ( questions ) ;
105
- /*
106
- * We use a separate inquirer prompt so that we can treat the
107
- * sourceCollectionPath as the default destinationCollectionPath. This
108
- * corresponds to the common use case in which the caller is import documents
109
- * from a collection that caller later plans to mirror changes for.
110
- */
111
- const {
112
- destinationCollectionPath,
105
+ tableId,
113
106
batchSize,
114
- } = await inquirer . prompt ( questionsRemaining ( sourceCollectionPath ) ) ;
107
+ } = await inquirer . prompt ( questions ) ;
115
108
116
109
const batch = parseInt ( batchSize ) ;
117
- const rawChangeLogName = `${ destinationCollectionPath } _raw_changelog` ;
110
+ const rawChangeLogName = `${ tableId } _raw_changelog` ;
118
111
119
112
// Initialize Firebase
120
113
firebase . initializeApp ( {
@@ -125,8 +118,10 @@ const run = async (): Promise<number> => {
125
118
process . env . PROJECT_ID = projectId ;
126
119
process . env . GOOGLE_CLOUD_PROJECT = projectId ;
127
120
121
+ // We pass in the application-level "tableId" here. The tracker determines
122
+ // the name of the raw changelog from this field.
128
123
const dataSink = new FirestoreBigQueryEventHistoryTracker ( {
129
- tableId : destinationCollectionPath ,
124
+ tableId : tableId ,
130
125
datasetId : datasetId ,
131
126
} ) ;
132
127
0 commit comments