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, starting from a given position
053     * within the event stream.
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     * @param  api    the API connection to use.
069     * @param  after  the lower bound on the timestamp of the events returned.
070     * @param  before the upper bound on the timestamp of the events returned.
071     * @param  types  an optional list of event types to filter by.
072     * @return        a log of all the events that met the given criteria.
073     */
074    public static EventLog getEnterpriseEvents(BoxAPIConnection api, Date after, Date before, BoxEvent.Type... types) {
075        return getEnterpriseEvents(api, null, after, before, ENTERPRISE_LIMIT, types);
076    }
077
078    /**
079     * Gets all the enterprise events that occurred within a specified date range, starting from a given position
080     * within the event stream.
081     * @param  api      the API connection to use.
082     * @param  position the starting position of the event stream.
083     * @param  after    the lower bound on the timestamp of the events returned.
084     * @param  before   the upper bound on the timestamp of the events returned.
085     * @param  limit    the number of entries to be returned in the response.
086     * @param  types    an optional list of event types to filter by.
087     * @return          a log of all the events that met the given criteria.
088     */
089    public static EventLog getEnterpriseEvents(BoxAPIConnection api, String position, Date after, Date before,
090                                               int limit, BoxEvent.Type... types) {
091
092        URL url = ENTERPRISE_EVENT_URL_TEMPLATE.build(api.getBaseURL());
093
094        if (position != null || types.length > 0 || after != null
095            || before != null || limit != ENTERPRISE_LIMIT) {
096            QueryStringBuilder queryBuilder = new QueryStringBuilder(url.getQuery());
097
098            if (after != null) {
099                queryBuilder.appendParam("created_after",
100                    BoxDateFormat.format(after));
101            }
102
103            if (before != null) {
104                queryBuilder.appendParam("created_before",
105                    BoxDateFormat.format(before));
106            }
107
108            if (position != null) {
109                queryBuilder.appendParam("stream_position", position);
110            }
111
112            if (limit != ENTERPRISE_LIMIT) {
113                queryBuilder.appendParam("limit", limit);
114            }
115
116            if (types.length > 0) {
117                StringBuilder filterBuilder = new StringBuilder();
118                for (BoxEvent.Type filterType : types) {
119                    filterBuilder.append(filterType.name());
120                    filterBuilder.append(',');
121                }
122                filterBuilder.deleteCharAt(filterBuilder.length() - 1);
123                queryBuilder.appendParam("event_type", filterBuilder.toString());
124            }
125
126            try {
127                url = queryBuilder.addToURL(url);
128            } catch (MalformedURLException e) {
129                throw new BoxAPIException("Couldn't append a query string to the provided URL.");
130            }
131        }
132
133        BoxAPIRequest request = new BoxAPIRequest(api, url, "GET");
134        BoxJSONResponse response = (BoxJSONResponse) request.send();
135        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
136        EventLog log = new EventLog(api, responseJSON, position, limit);
137        log.setStartDate(after);
138        log.setEndDate(before);
139        return log;
140    }
141
142    void setStartDate(Date startDate) {
143        this.startDate = startDate;
144    }
145
146    void setEndDate(Date endDate) {
147        this.endDate = endDate;
148    }
149
150    /**
151     * Returns an iterator over the events in this log.
152     * @return an iterator over the events in this log.
153     */
154    @Override
155    public Iterator<BoxEvent> iterator() {
156        return this.set.iterator();
157    }
158
159    /**
160     * Gets the date of the earliest event in this log.
161     *
162     * <p>The value returned by this method corresponds to the <code>created_after</code> URL parameter that was used
163     * when retrieving the events in this EventLog.</p>
164     *
165     * @return the date of the earliest event in this log.
166     */
167    public Date getStartDate() {
168        return this.startDate;
169    }
170
171    /**
172     * Gets the date of the latest event in this log.
173     *
174     * <p>The value returned by this method corresponds to the <code>created_before</code> URL parameter that was used
175     * when retrieving the events in this EventLog.</p>
176     *
177     * @return the date of the latest event in this log.
178     */
179    public Date getEndDate() {
180        return this.endDate;
181    }
182
183    /**
184     * Gets the maximum number of events that this event log could contain given its start date, end date, and stream
185     * position.
186     *
187     * <p>The value returned by this method corresponds to the <code>limit</code> URL parameter that was used when
188     * retrieving the events in this EventLog.</p>
189     *
190     * @return the maximum number of events.
191     */
192    public int getLimit() {
193        return this.limit;
194    }
195
196    /**
197     * Gets the starting position of the events in this log within the event stream.
198     *
199     * <p>The value returned by this method corresponds to the <code>stream_position</code> URL parameter that was used
200     * when retrieving the events in this EventLog.</p>
201     *
202     * @return the starting position within the event stream.
203     */
204    public String getStreamPosition() {
205        return this.streamPosition;
206    }
207
208    /**
209     * Gets the next position within the event stream for retrieving subsequent events.
210     *
211     * <p>The value returned by this method corresponds to the <code>next_stream_position</code> field returned by the
212     * API's events endpoint.</p>
213     *
214     * @return the next position within the event stream.
215     */
216    public String getNextStreamPosition() {
217        return this.nextStreamPosition;
218    }
219
220    /**
221     * Gets the number of events in this log, including duplicate events.
222     *
223     * <p>The chunk size may not be representative of the number of events returned by this EventLog's iterator because
224     * the iterator will automatically ignore duplicate events.</p>
225     *
226     * <p>The value returned by this method corresponds to the <code>chunk_size</code> field returned by the API's
227     * events endpoint.</p>
228     *
229     * @return the number of events, including duplicates.
230     */
231    public int getChunkSize() {
232        return this.chunkSize;
233    }
234
235    /**
236     * Gets the number of events in this list, excluding duplicate events.
237     *
238     * <p>The size is guaranteed to be representative of the number of events returned by this EventLog's iterator.</p>
239     *
240     * @return the number of events, excluding duplicates.
241     */
242    public int getSize() {
243        return this.set.size();
244    }
245}