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