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