1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2017 Couchbase, Inc
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 #pragma once
19 
20 #include <cJSON.h>
21 #include <algorithm>
22 #include <cstdint>
23 #include <string>
24 #include <vector>
25 
26 #include "collections/collections_types.h"
27 
28 namespace Collections {
29 
30 /**
31  * Manifest is an object that is constructed from JSON data as per
32  * a set_collections command
33  *
34  * Users of this class can then obtain the revision, separator and
35  * all collections that are included in the manifest.
36  */
37 class Manifest {
38 public:
39     // Manfifest::Identifier stores/owns name
40     class Identifier : public IdentifierInterface<Identifier> {
41     public:
Identifier(const std::string& name, uid_t uid)42         Identifier(const std::string& name, uid_t uid) : name(name), uid(uid) {
43         }
44 
45         template <class T>
Identifier(const IdentifierInterface<T>& identifier)46         Identifier(const IdentifierInterface<T>& identifier)
47             : name(identifier.getName().data(), identifier.getName().size()),
48               uid(identifier.getUid()) {
49         }
50 
getName() const51         cb::const_char_buffer getName() const {
52             return {name};
53         }
54 
getUid() const55         uid_t getUid() const {
56             return uid;
57         }
58 
operator ==(const Collections::Identifier& rhs) const59         bool operator==(const Collections::Identifier& rhs) const {
60             return rhs.getName() == name.c_str() && rhs.getUid() == uid;
61         }
62 
63     private:
64         std::string name;
65         uid_t uid;
66     };
67 
68     /*
69      * Create a manifest from json.
70      * Validates the json as per SET_COLLECTIONS rules.
71      * @param json a string containing the JSON data
72      * @param maxNumberOfCollections an upper limit on the number of collections
73      *        allowed, defaults to 1000.
74      */
75     Manifest(const std::string& json, size_t maxNumberOfCollections = 1000);
76 
getSeparator() const77     const std::string& getSeparator() const {
78         return separator;
79     }
80 
doesDefaultCollectionExist() const81     bool doesDefaultCollectionExist() const {
82         return defaultCollectionExists;
83     }
84 
85     using container = std::vector<Identifier>;
86 
begin() const87     container::const_iterator begin() const {
88         return collections.begin();
89     }
90 
end() const91     container::const_iterator end() const {
92         return collections.end();
93     }
94 
size() const95     size_t size() const {
96         return collections.size();
97     }
98 
99     /// return the UID of the Manifest
getUid() const100     uid_t getUid() const {
101         return uid;
102     }
103 
104     /**
105      * Try and find an identifier in this Manifest (name/uid match)
106      * @return iterator to the matching entry or end() if not found.
107      */
find( const Collections::Identifier& identifier) const108     container::const_iterator find(
109             const Collections::Identifier& identifier) const {
110         return std::find_if(
111                 begin(), end(), [identifier](const Identifier& entry) {
112                     return entry == identifier;
113                 });
114     }
115 
116     /// @todo enhance filters with UIDs - this find remains for Filter code
find(const char* name) const117     container::const_iterator find(const char* name) const {
118         return std::find_if(begin(), end(), [name](const Identifier& entry) {
119             return entry.getName() == name;
120         });
121     }
122 
123     /**
124      * @returns this manifest as a std::string (JSON formatted)
125      */
126     std::string toJson() const;
127 
128     /**
129      * Write to std::cerr this
130      */
131     void dump() const;
132 
133 private:
134     /**
135      * Set defaultCollectionExists to true if name matches $default
136      * @param name C-string collection name
137      */
138     void enableDefaultCollection(const char* name);
139 
140     /**
141      * Get json sub-object from the json object for key and check the type.
142      * @param json The parent object in which to find key.
143      * @param key The key to look for.
144      * @param expectedType The type the found object must be.
145      * @return A cJSON object for key.
146      * @throws std::invalid_argument if key is not found or the wrong type.
147      */
148     cJSON* getJsonObject(cJSON* json, const char* key, int expectedType);
149 
150     /**
151      * Constructor helper function, throws invalid_argument with a string
152      * indicating if the cJSON struct is nullptr or not the expectedType.
153      *
154      * @param errorKey the JSON key associated with the cJSON struct
155      * @param cJsonHandle the struct handed back by cJSON functions
156      * @param expectedType the cJSON type we expect cJsonHandle to be
157      * @throws std::invalid_argument if cJsonHandle is null or !expectedType
158      */
159     static void throwIfNullOrWrongType(const std::string& errorKey,
160                                        cJSON* cJsonHandle,
161                                        int expectedType);
162 
163     /**
164      * Check if the C-string input has a length > 0 and < 250.
165      *
166      * @param separator a C-string representing the separator.
167      */
168     static bool validSeparator(const char* separator);
169 
170     /**
171      * Check if the C-string represents a legal collection name.
172      * Current validation is to ensure we block creation of _ prefixed
173      * collections and only accept $default for $ prefixed names.
174      *
175      * @param collection a C-string representing a collection name.
176      */
177     static bool validCollection(const char* collection);
178 
179     friend std::ostream& operator<<(std::ostream& os, const Manifest& manifest);
180 
181     bool defaultCollectionExists;
182     std::string separator;
183     container collections;
184     uid_t uid;
185 
186     // strings used in JSON parsing
187     static constexpr char const* SeparatorKey = "separator";
188     static constexpr int SeparatorType = cJSON_String;
189     static constexpr char const* CollectionsKey = "collections";
190     static constexpr int CollectionsType = cJSON_Array;
191     static constexpr char const* CollectionNameKey = "name";
192     static constexpr int CollectionNameType = cJSON_String;
193     static constexpr char const* CollectionUidKey = "uid";
194     static constexpr int CollectionUidType = cJSON_String;
195 };
196 
197 std::ostream& operator<<(std::ostream& os, const Manifest& manifest);
198 
199 } // end namespace Collections
200