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
28namespace 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 */
37class Manifest {
38public:
39    // Manfifest::Identifier stores/owns name
40    class Identifier : public IdentifierInterface<Identifier> {
41    public:
42        Identifier(const std::string& name, uid_t uid) : name(name), uid(uid) {
43        }
44
45        template <class T>
46        Identifier(const IdentifierInterface<T>& identifier)
47            : name(identifier.getName().data(), identifier.getName().size()),
48              uid(identifier.getUid()) {
49        }
50
51        cb::const_char_buffer getName() const {
52            return {name};
53        }
54
55        uid_t getUid() const {
56            return uid;
57        }
58
59        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
77    const std::string& getSeparator() const {
78        return separator;
79    }
80
81    bool doesDefaultCollectionExist() const {
82        return defaultCollectionExists;
83    }
84
85    using container = std::vector<Identifier>;
86
87    container::const_iterator begin() const {
88        return collections.begin();
89    }
90
91    container::const_iterator end() const {
92        return collections.end();
93    }
94
95    size_t size() const {
96        return collections.size();
97    }
98
99    /// return the UID of the Manifest
100    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     */
108    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
117    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
133private:
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
197std::ostream& operator<<(std::ostream& os, const Manifest& manifest);
198
199} // end namespace Collections
200