001package com.box.sdk;
002
003import com.eclipsesource.json.JsonArray;
004import com.eclipsesource.json.JsonObject;
005import com.eclipsesource.json.JsonValue;
006import java.net.MalformedURLException;
007import java.net.URL;
008import java.util.Date;
009import java.util.Iterator;
010import java.util.LinkedHashSet;
011import java.util.Set;
012
013/**
014 * A log of events that were retrieved from the events endpoint.
015 *
016 * <p>An EventLog cannot be instantiated directly. Instead, use one of the static methods to retrieve a log of events.
017 * Unlike the {@link EventStream} class, EventLog doesn't support retrieving events in real-time.
018 * </p>
019 */
020public class EventLog implements Iterable<BoxEvent> {
021
022    private static final int ENTERPRISE_LIMIT = 500;
023    /**
024     * Enterprise Event URL Template.
025     */
026    public static final URLTemplate ENTERPRISE_EVENT_URL_TEMPLATE = new URLTemplate("events?stream_type=admin_logs&"
027        + "limit=" + ENTERPRISE_LIMIT);
028    private final int chunkSize;
029    private final int limit;
030    private final String nextStreamPosition;
031    private final String streamPosition;
032    private final Set<BoxEvent> set;
033
034    private Date startDate;
035    private Date endDate;
036
037    EventLog(BoxAPIConnection api, JsonObject json, String streamPosition, int limit) {
038        this.streamPosition = streamPosition;
039        this.limit = limit;
040        this.nextStreamPosition = json.get("next_stream_position").asString();
041        this.chunkSize = json.get("chunk_size").asInt();
042
043        this.set = new LinkedHashSet<BoxEvent>(this.chunkSize);
044        JsonArray entries = json.get("entries").asArray();
045        for (JsonValue entry : entries) {
046            this.set.add(new BoxEvent(api, entry.asObject()));
047        }
048    }
049
050    /**
051     * Gets all the enterprise events that occurred within a specified date range, starting from a given position
052     * within the event stream.
053     *
054     * @param api      the API connection to use.
055     * @param position the starting position of the event stream.
056     * @param after    the lower bound on the timestamp of the events returned.
057     * @param before   the upper bound on the timestamp of the events returned.
058     * @param types    an optional list of event types to filter by.
059     * @return a log of all the events that met the given criteria.
060     */
061    public static EventLog getEnterpriseEvents(BoxAPIConnection api, String position, Date after, Date before,
062                                               BoxEvent.Type... types) {
063        return getEnterpriseEvents(api, position, after, before, ENTERPRISE_LIMIT, types);
064    }
065
066    /**
067     * Gets all the enterprise events that occurred within a specified date range.
068     *
069     * @param api    the API connection to use.
070     * @param after  the lower bound on the timestamp of the events returned.
071     * @param before the upper bound on the timestamp of the events returned.
072     * @param types  an optional list of event types to filter by.
073     * @return a log of all the events that met the given criteria.
074     */
075    public static EventLog getEnterpriseEvents(BoxAPIConnection api, Date after, Date before, BoxEvent.Type... types) {
076        return getEnterpriseEvents(api, null, after, before, ENTERPRISE_LIMIT, types);
077    }
078
079    /**
080     * Gets all the enterprise events that occurred within a specified date range, starting from a given position
081     * within the event stream.
082     *
083     * @param api      the API connection to use.
084     * @param position the starting position of the event stream.
085     * @param after    the lower bound on the timestamp of the events returned.
086     * @param before   the upper bound on the timestamp of the events returned.
087     * @param limit    the number of entries to be returned in the response.
088     * @param types    an optional list of event types to filter by.
089     * @return a log of all the events that met the given criteria.
090     */
091    public static EventLog getEnterpriseEvents(BoxAPIConnection api, String position, Date after, Date before,
092                                               int limit, BoxEvent.Type... types) {
093
094        URL url = ENTERPRISE_EVENT_URL_TEMPLATE.build(api.getBaseURL());
095
096        if (position != null || types.length > 0 || after != null
097            || before != null || limit != ENTERPRISE_LIMIT) {
098            QueryStringBuilder queryBuilder = new QueryStringBuilder(url.getQuery());
099
100            if (after != null) {
101                queryBuilder.appendParam("created_after",
102                    BoxDateFormat.format(after));
103            }
104
105            if (before != null) {
106                queryBuilder.appendParam("created_before",
107                    BoxDateFormat.format(before));
108            }
109
110            if (position != null) {
111                queryBuilder.appendParam("stream_position", position);
112            }
113
114            if (limit != ENTERPRISE_LIMIT) {
115                queryBuilder.appendParam("limit", limit);
116            }
117
118            if (types.length > 0) {
119                StringBuilder filterBuilder = new StringBuilder();
120                for (BoxEvent.Type filterType : types) {
121                    filterBuilder.append(filterType.name());
122                    filterBuilder.append(',');
123                }
124                filterBuilder.deleteCharAt(filterBuilder.length() - 1);
125                queryBuilder.appendParam("event_type", filterBuilder.toString());
126            }
127
128            try {
129                url = queryBuilder.addToURL(url);
130            } catch (MalformedURLException e) {
131                throw new BoxAPIException("Couldn't append a query string to the provided URL.");
132            }
133        }
134
135        BoxAPIRequest request = new BoxAPIRequest(api, url, "GET");
136        BoxJSONResponse response = (BoxJSONResponse) request.send();
137        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
138        EventLog log = new EventLog(api, responseJSON, position, limit);
139        log.setStartDate(after);
140        log.setEndDate(before);
141        return log;
142    }
143
144    /**
145     * Returns an iterator over the events in this log.
146     *
147     * @return an iterator over the events in this log.
148     */
149    @Override
150    public Iterator<BoxEvent> iterator() {
151        return this.set.iterator();
152    }
153
154    /**
155     * Gets the date of the earliest event in this log.
156     *
157     * <p>The value returned by this method corresponds to the <code>created_after</code> URL parameter that was used
158     * when retrieving the events in this EventLog.</p>
159     *
160     * @return the date of the earliest event in this log.
161     */
162    public Date getStartDate() {
163        return this.startDate;
164    }
165
166    void setStartDate(Date startDate) {
167        this.startDate = startDate;
168    }
169
170    /**
171     * Gets the date of the latest event in this log.
172     *
173     * <p>The value returned by this method corresponds to the <code>created_before</code> URL parameter that was used
174     * when retrieving the events in this EventLog.</p>
175     *
176     * @return the date of the latest event in this log.
177     */
178    public Date getEndDate() {
179        return this.endDate;
180    }
181
182    void setEndDate(Date endDate) {
183        this.endDate = endDate;
184    }
185
186    /**
187     * Gets the maximum number of events that this event log could contain given its start date, end date, and stream
188     * position.
189     *
190     * <p>The value returned by this method corresponds to the <code>limit</code> URL parameter that was used when
191     * retrieving the events in this EventLog.</p>
192     *
193     * @return the maximum number of events.
194     */
195    public int getLimit() {
196        return this.limit;
197    }
198
199    /**
200     * Gets the starting position of the events in this log within the event stream.
201     *
202     * <p>The value returned by this method corresponds to the <code>stream_position</code> URL parameter that was used
203     * when retrieving the events in this EventLog.</p>
204     *
205     * @return the starting position within the event stream.
206     */
207    public String getStreamPosition() {
208        return this.streamPosition;
209    }
210
211    /**
212     * Gets the next position within the event stream for retrieving subsequent events.
213     *
214     * <p>The value returned by this method corresponds to the <code>next_stream_position</code> field returned by the
215     * API's events endpoint.</p>
216     *
217     * @return the next position within the event stream.
218     */
219    public String getNextStreamPosition() {
220        return this.nextStreamPosition;
221    }
222
223    /**
224     * Gets the number of events in this log, including duplicate events.
225     *
226     * <p>The chunk size may not be representative of the number of events returned by this EventLog's iterator because
227     * the iterator will automatically ignore duplicate events.</p>
228     *
229     * <p>The value returned by this method corresponds to the <code>chunk_size</code> field returned by the API's
230     * events endpoint.</p>
231     *
232     * @return the number of events, including duplicates.
233     */
234    public int getChunkSize() {
235        return this.chunkSize;
236    }
237
238    /**
239     * Gets the number of events in this list, excluding duplicate events.
240     *
241     * <p>The size is guaranteed to be representative of the number of events returned by this EventLog's iterator.</p>
242     *
243     * @return the number of events, excluding duplicates.
244     */
245    public int getSize() {
246        return this.set.size();
247    }
248}